package tool

import (
	"database/sql"
	"encoding/json"
	"gitee.com/cdsailyj/go-vpn/common/util"
	"github.com/gin-gonic/gin"
	"log"
	"math"
	"math/rand"
	"net/http"
	"os"
	"os/exec"
	"path/filepath"
	"reflect"
	"runtime"
	"strings"
	"time"
)

// JSON成功
func AjaxSuccess(c *gin.Context, data any) {
	c.JSON(http.StatusOK, gin.H{"code": 0, "data": data, "msg": "success"})
}

// JSON失败
func AjaxError(c *gin.Context, code int, msg string) {
	c.JSON(http.StatusOK, gin.H{"code": code, "msg": msg})
}

func GetAppPath() string {
	if IsRelease() {
		exePath, _ := os.Executable()
		return filepath.Dir(exePath) + "/"
	} else {
		pwd, _ := os.Getwd()
		return pwd + "/"
	}
}

func IsRelease() bool {
	arg1 := strings.ToLower(os.Args[0])
	return strings.Index(arg1, "go-build") < 0
}

// 注册的Logger
var loggerMap = make(map[string]*log.Logger)
var loggerUtilMap = make(map[string]*util.LoggerUtil)

// GetLogger 获取一个新Logger
func GetLogger(file string, prefix string) (*log.Logger, error) {
	_, ok := loggerMap[file]
	if !ok {
		f, err := OpeFile(file, 0777)
		if err != nil {
			return nil, err
		}
		logger := log.New(f, prefix, log.Ldate|log.Ltime|log.Lshortfile)
		loggerMap[file] = logger
	}

	return loggerMap[file], nil
}

// GetLogger 获取一个新Logger
func GetLoggerUtil(file string, prefix string) (*util.LoggerUtil, error) {
	_, ok := loggerUtilMap[file]
	if !ok {
		f, err := OpeFile(file, 0777)
		if err != nil {
			return nil, err
		}
		logger := &util.LoggerUtil{
			File:   f,
			Logger: log.New(f, prefix, log.Ldate|log.Ltime|log.Lshortfile),
		}
		loggerUtilMap[file] = logger
	}

	return loggerUtilMap[file], nil
}

// OpeFile 打开文件，没有回自动创建
func OpeFile(filePath string, mode os.FileMode) (*os.File, error) {
	// 判断目录是否存在
	if _, err := os.Stat(filepath.Dir(filePath)); os.IsNotExist(err) {
		// 目录不存在，创建
		err = os.MkdirAll(filepath.Dir(filePath), mode)
		if err != nil {
			return nil, err
		}
	}

	//if _, err := os.Stat(filePath); os.IsNotExist(err) {
	//	// 文件不存在，创建
	//	file, err := os.Create(filePath)
	//	return file, err
	//}

	file, err := os.OpenFile(filePath, os.O_CREATE|os.O_RDWR|os.O_APPEND, mode)
	return file, err
}

func ShouldLog(path string) bool {
	// 定义要忽略的扩展名和路径
	ignoreExts := []string{".js", ".css", ".png", ".jpg", ".jpeg", ".ico", ".svg", ".woff", ".ttf", ".map"}
	ignorePaths := []string{"/assets/", "/static/", "/favicon.ico"}

	for _, ext := range ignoreExts {
		if strings.HasSuffix(path, ext) {
			return false
		}
	}

	for _, prefix := range ignorePaths {
		if strings.HasPrefix(path, prefix) {
			return false
		}
	}

	return true
}

// Datetime 格式化时间
func Datetime(t time.Time) string {
	return t.Format("2006-01-02 15:04:05")
}

// GetMapKeys 获取map的所有key
func GetMapKeys(m map[string]interface{}) []string {
	var keyArr []string
	for k, _ := range m {
		keyArr = append(keyArr, k)
	}
	return keyArr
}

// OpenBrowser 使用默认浏览器打开
func OpenBrowser(url string) error {
	var cmd *exec.Cmd

	switch runtime.GOOS {
	case "linux":
		cmd = exec.Command("xdg-open", url)
	case "windows":
		cmd = exec.Command("cmd", "/c", "start", url)
	case "darwin":
		cmd = exec.Command("open", url)
	default:
		log.Fatalf("Unsupported operating system: %s", runtime.GOOS)
	}

	err := cmd.Start()
	return err
}

// StructToMapJSON 结构体转化为map，通过JSON序列化实现
func StructToMapJSON(obj interface{}) (map[string]interface{}, error) {
	jsonData, err := json.Marshal(obj)
	if err != nil {
		return nil, err
	}

	var result map[string]interface{}
	err = json.Unmarshal(jsonData, &result)
	if err != nil {
		return nil, err
	}

	return result, nil
}

// 判断是否是指针，是的话，就转化为值
func DerefIfPtr(v any) any {
	// 首先检查接口本身是否为 nil
	if v == nil {
		return nil
	}

	val := reflect.ValueOf(v)

	// 检查 reflect.Value 是否有效
	if !val.IsValid() {
		return nil
	}

	// 处理指针类型
	if val.Kind() == reflect.Ptr {
		// 检查指针是否为 nil
		if val.IsNil() {
			return nil
		}
		// 安全解引用
		elem := val.Elem()
		if elem.IsValid() {
			return elem.Interface()
		}
		return nil
	}

	// 非指针类型直接返回
	return v
}

// 获取结构体中的实际值
func StructValue(v any) any {
	if num, ok := v.(sql.NullString); ok {
		return If(num.Valid, num.String, "")
	}
	if num, ok := v.(sql.NullFloat64); ok {
		return If(num.Valid, num.Float64, 0)
	}
	if num, ok := v.(sql.NullInt16); ok {
		return If(num.Valid, num.Int16, 0)
	}
	if num, ok := v.(sql.NullInt32); ok {
		return If(num.Valid, num.Int32, 0)
	}
	if num, ok := v.(sql.NullInt64); ok {
		return If(num.Valid, num.Int64, 0)
	}

	return v
}

// StructToMap 结构体转化为map
func StructToMap(obj interface{}) map[string]interface{} {
	obj1 := reflect.TypeOf(obj)
	obj2 := reflect.ValueOf(obj)

	data := make(map[string]interface{})
	for i := 0; i < obj1.NumField(); i++ {
		if obj1.Field(i).Tag.Get("mapstructure") != "" {
			data[obj1.Field(i).Tag.Get("mapstructure")] = DerefIfPtr(obj2.Field(i).Interface())
		} else {
			data[obj1.Field(i).Name] = DerefIfPtr(obj2.Field(i).Interface())
		}
	}
	return data
}

// StructToMapList 结构体转化为map list
func StructToMapList(data *[]interface{}) []map[string]interface{} {
	var result []map[string]interface{}
	for _, item := range *data {
		itemMap := make(map[string]interface{})
		val := reflect.ValueOf(item)
		if val.Kind() == reflect.Ptr {
			val = val.Elem()
		}
		if val.Kind() != reflect.Struct {
			continue
		}

		typ := val.Type()
		for i := 0; i < val.NumField(); i++ {
			field := typ.Field(i)
			tag := field.Tag.Get("json")
			if tag == "" {
				tag = field.Name
			}

			itemMap[tag] = val.Field(i).Interface()
		}

		result = append(result, itemMap)
	}

	return result
}

// RandRangeInt 范围随机数
func RandRangeInt(min int, max int) int {
	return rand.Intn(max-min+1) + min
}

// IfBool 模拟三元
func IfBool(b bool, val1 bool, val2 bool) bool {
	if b {
		return val1
	} else {
		return val2
	}
}

func IfString(b bool, val1 string, val2 string) string {
	if b {
		return val1
	} else {
		return val2
	}
}

func IfInt(b bool, val1 int, val2 int) int {
	if b {
		return val1
	} else {
		return val2
	}
}

func IfInt64(b bool, val1 int64, val2 int64) int64 {
	if b {
		return val1
	} else {
		return val2
	}
}

func If[T any](b bool, val1 T, val2 T) T {
	if b {
		return val1
	} else {
		return val2
	}
}

// chunkSlice 将一个切片分割成指定大小的多个子切片
func ChunkSlice[T any](slice []T, chunkSize int) [][]T {
	var chunks [][]T
	for i := 0; i < len(slice); i += chunkSize {
		end := i + chunkSize
		if end > len(slice) {
			end = len(slice)
		}
		chunks = append(chunks, slice[i:end])
	}
	return chunks
}

func ChunkByThread[T any](data []T, threadNum int) [][]T {
	chunkSize := math.Ceil(float64(len(data)) / float64(threadNum))
	chunkArr := ChunkSlice(data, int(chunkSize))
	return chunkArr
}
